OAuth 2.0 Protected Resource Metadata endpoint#260773
OAuth 2.0 Protected Resource Metadata endpoint#260773elena-shostak wants to merge 17 commits intoelastic:mainfrom
Conversation
x-pack/platform/plugins/shared/security/server/routes/oauth_metadata/protected_resource.ts
Show resolved
Hide resolved
a031945 to
ff74af2
Compare
| const expectedAudience = this.#kibanaServerURL.endsWith('/') | ||
| ? this.#kibanaServerURL | ||
| : `${this.#kibanaServerURL}/`; |
There was a problem hiding this comment.
Important
Claude's client normalizes the URL to the canonical form with an explicit path. UIAM should treat these as equivalent when comparing, but it doesn't right now, so appending trailing slash as temporary fix, otherwise we would get
Audience mismatch: expected http://localhost:5601 but token has http://localhost:5601/
There was a problem hiding this comment.
Let's leave a TODO comment to record that it's a temporary workaround that we should remove.
|
Pinging @elastic/kibana-security (Team:Security) |
💛 Build succeeded, but was flaky
Failed CI StepsTest Failures
Metrics [docs]Public APIs missing comments
Page load bundle
History
|
|
ACK: will review today |
azasypkin
left a comment
There was a problem hiding this comment.
Tested locally in Claude Desktop using the config below. The authentication flow and subsequent tool calls worked correctly. LGTM! Just a few nits and questions.
{
"mcpServers": {
"oauth-server": {
"command": "npx",
"args": [
"mcp-remote",
"http://localhost:5601/api/agent_builder/mcp",
"--static-oauth-client-info",
"{ \"client_id\": \"XXXX\" }",
"--static-oauth-client-metadata",
"{ \"scope\": \"all\" }"
],
"env": {
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
}
}
}| '--env', | ||
| 'UIAM_SERVICE_BOUNDARY=external', |
There was a problem hiding this comment.
nit: we don't need this for cosmos db?
| '--env', | |
| 'UIAM_SERVICE_BOUNDARY=external', |
| `${KBN_CERT_PATH}:/tmp/server.crt:z`, | ||
|
|
||
| '-p', | ||
| `127.0.0.1:${+new URL(MOCK_IDP_UIAM_SERVICE_INTERNAL_URL)?.port + 1}:8443`, // UIAM OAuth HTTPS port |
There was a problem hiding this comment.
nit: now that we two UAIM containers, I think it's time to remove this +new URL(url)?.port and +new URL(url)?.port + 1 logic and just define ports separately for all containers:
const ENV_DEFAULTS = {
UIAM_COSMOS_DB_PORT: '8081',
UIAM_COSMOS_DB_UI_PORT: '8082',
UIAM_SERVICE_PORT: '8443',
UIAM_OAUTH_SERVICE_PORT: '8444',
UIAM_APP_LOGGING_LEVEL: 'DEBUG',
UIAM_LOGGING_LEVEL: 'INFO',
};
And then just use env.UIAM_COSMOS_DB_PORT, env.UIAM_SERVICE_PORT, and env.UIAM_OAUTH_SERVICE_PORT.
What do you think?
| * UIAM external (OAuth) container needs a filesystem path to IdP metadata XML. | ||
| * Override with `MOCK_IDP_KIBANA_URL`. | ||
| */ | ||
| async function extraDockerParamsForUiamOauthContainer(): Promise<string[]> { |
There was a problem hiding this comment.
question: cannot we define these params directly on UIAM_OAUTH_CONTAINER, now that we don't write a custom metadata file?
| * Returns the list of UIAM containers to run. | ||
| * When `uiamOAuth` is true, includes the UIAM OAuth container. | ||
| */ | ||
| export function getUiamContainers(uiamOAuth?: boolean): UiamContainer[] { |
There was a problem hiding this comment.
nit: unnamed bool params are a bit hard to read in the consumer code, what about { includeUiamOAuth: boolean } or something like this?
| typeof issuerElement === 'string' ? issuerElement : issuerElement?._ ?? undefined; | ||
| return { | ||
| requestId: attrs.ID as string, | ||
| acsUrl: attrs.AssertionConsumerServiceURL as string | undefined, |
There was a problem hiding this comment.
optional nit: if it helps to simplify the code here and in MockIdP, we can always hard code ACS as kibana URL + fixed /api/security/saml/callback as we shouldn't support anything else, but feel free to keep everything as is.
| } | ||
| ); | ||
|
|
||
| // MCP SDK (RFC 9728) tries path-aware discovery first (e.g., |
There was a problem hiding this comment.
nit: direct link to a relevant section of the spec here would also be helpful.
which the SDK
What SDK is meant here?
{path*} vs /.well-known/oauth-protected-resource/api/agent_builder/mcp
Would it make sense to validate that if path is provided it corresponds to /api/agent_builder/mcp?
| schema.literal('header'), | ||
| schema.literal('body'), | ||
| schema.literal('query'), |
There was a problem hiding this comment.
nit: would be great to leave a code comment that explains what all these 3 values mean. Do we/UIAM support all of these?
Other fields would probably also benefit from same brief docs.
| }); | ||
|
|
||
| export type UiamConfigType = TypeOf<typeof ConfigSchema>['uiam']; | ||
| export type McpConfigType = TypeOf<typeof ConfigSchema>['mcp']; |
There was a problem hiding this comment.
nit: it doesn't look we use it anywhere.
| export type McpConfigType = TypeOf<typeof ConfigSchema>['mcp']; |
| : this.authenticator.getRequestOriginalURL(request); | ||
|
|
||
| // For routes that accept UIAM OAuth tokens, return a 401 with a WWW-Authenticate header | ||
| // containing the resource_metadata URL (RFC 9728) instead of redirecting to the login page. |
There was a problem hiding this comment.
nit: let's leave a direct link to a relevant part of the spec.
| body: JSON.stringify({ | ||
| jsonrpc: '2.0', | ||
| id: null, | ||
| error: { code: -32001, message: 'Unauthorized' }, |
There was a problem hiding this comment.
question: what does this code mean and where is it coming from?
Summary
OAuth 2.0 Protected Resource Metadata endpoint https://datatracker.ietf.org/doc/rfc9728/
How to test
Note
You may need to add self signed certificate to
claude/settings.json"NODE_EXTRA_CA_CERTS": "<path_to_kibana>/src/platform/packages/shared/kbn-dev-utils/certs/ca.crt"You should get a clientId with response
{ "id": "<your_client_id>" "type": "public", "resource": "http://localhost:5601/", "creation": "2026-04-01T14:56:04.184718009Z", "revoked": false, "connections": { "active": [], "revoked": [] }, "client_name": "Claude Code", "client_metadata": {} }Should output
/mcpto start OAuth flowScreen.Recording.2026-04-08.at.13.23.44.mov
Checklist
Closes: https://github.com/elastic/kibana-team/issues/2750
Relates: #256182